package tx.transaction;

import cn.stimd.spring.jdbc.datasource.ConnectionHolder;
import cn.stimd.spring.jdbc.datasource.DataSourceUtils;
import cn.stimd.spring.transaction.TransactionDefinition;
import cn.stimd.spring.transaction.TransactionException;
import cn.stimd.spring.transaction.TransactionStatus;
import cn.stimd.spring.transaction.support.DefaultTransactionDefinition;
import cn.stimd.spring.transaction.support.DefaultTransactionStatus;
import cn.stimd.spring.transaction.support.TransactionSynchronizationManager;

import javax.sql.DataSource;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.SQLException;

/**
 * Created by Stimd
 * 项目地址：https://gitee.com/stimd/spring-wheel
 * 公众号：Java编程探微
 */
public class TransactionMethod {
    private final Object target;
    private final Method method;
    private final Object[] args;
    private final TransactionStatus status;
    private final TransactionDefinition definition;
    private final DataSource dataSource;
    private TransactionMethod next;


    public TransactionMethod(Object target, Method method, Object[] args, boolean isNewTransaction, DataSource dataSource) {
        this.target = target;
        this.method = method;
        this.args = args;
        this.status = new DefaultTransactionStatus(new MyTransactionObject(), null, isNewTransaction, true, false);;
        this.definition = new DefaultTransactionDefinition();
        this.dataSource = dataSource;
    }

    public void execute() throws Exception {
        doBegin();
        try{
            method.invoke(target, args);

            if (next != null) {
                next.execute(); //执行下一个事务方法
            }
        }catch (Exception e) {
            doRollback();
            throw e;
        }
        doCommit();
    }


    private void doBegin() throws SQLException {
        //1. 如果是新事务，则获取数据库连接，并绑定到线程上
        if (definition != null && status.isNewTransaction()) {
            Connection connection = DataSourceUtils.getConnection(dataSource);
            connection.setAutoCommit(false);

            TransactionSynchronizationManager.bindResource(dataSource, new ConnectionHolder(connection));
            System.out.println("开启事务:" + method);
        }

        //2. 将 ConnectionHolder 绑定到事务对象上
        ConnectionHolder holder = (ConnectionHolder) TransactionSynchronizationManager.getResource(this.dataSource);
        MyTransactionObject txObject = (MyTransactionObject) ((DefaultTransactionStatus) status).getTransaction();
        txObject.setConnectionHolder(holder);
    }


    private void doRollback2() throws SQLException {
        ConnectionHolder holder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
        if (status.isNewTransaction()) {
            holder.getConnection().rollback();
            TransactionSynchronizationManager.unbindResource(dataSource);
            System.out.println("回滚事务:" + method);
        }else {
            holder.setRollbackOnly();
            System.out.println("仅标记回滚:" + method);
        }
    }

    private void doRollback() throws SQLException {
        //1) 如果是新事务，执行回滚操作
        if (status.isNewTransaction()) {
            ConnectionHolder holder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
            holder.getConnection().rollback();
            TransactionSynchronizationManager.unbindResource(dataSource);
            System.out.println("回滚事务:" + method);
        }
        //2) 内层方法仅标记回滚
        else {
            MyTransactionObject txObject = (MyTransactionObject) ((DefaultTransactionStatus) status).getTransaction();
            txObject.setRollbackOnly();
            System.out.println("仅标记回滚:" + method);
        }
    }


    private void doCommit() throws SQLException {
        //1) 如果全局回滚标记为true，转入rollback流程
        if(((DefaultTransactionStatus) status).isGlobalRollbackOnly()) {
            doRollback();

            if(status.isNewTransaction()) {
                throw new TransactionException("事务被标记为rollback-only，发生回滚");
            }
            return;
        }

        //2) 如果是新事务，执行提交（内层事务的commit操作不处理）
        if(status.isNewTransaction()) {
            ConnectionHolder holder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
            holder.getConnection().commit();
            System.out.println("提交事务:" + method);
        }
    }


    private void doCommit2() throws SQLException {
        //
        if (status.isNewTransaction()) {
            ConnectionHolder holder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
            if (holder.isRollbackOnly()) {
                doRollback();
            } else {
                holder.getConnection().commit();
                System.out.println("提交事务:" + method);
            }

            TransactionSynchronizationManager.unbindResource(dataSource);
        }
    }


    public void next(TransactionMethod method) {
        this.next = method;
    }
}
